/*-------------------------------------------------------------------------
 *
 * nodeWorktablescan.c
 *	  处理WorkTableScan节点的例程。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeWorktablescan.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "executor/execdebug.h"
#include "executor/nodeWorktablescan.h"

static TupleTableSlot *fc_WorkTableScanNext(WorkTableScanState *fc_node);

/* ----------------------------------------------------------------
 *		WorkTableScanNext
 *
 *		这是 ExecWorkTableScan 的工作马
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_WorkTableScanNext(WorkTableScanState *fc_node)
{
	TupleTableSlot *fc_slot;
	Tuplestorestate *fc_tuplestorestate;

	/*
	 * 从 estate 和扫描状态获取信息
	 *
	 * 注意：我们故意不支持向后扫描。 尽管这里只需多几行代码，但这会迫使 nodeRecursiveunion.c 在启用向后扫描的情况下创建元组存储，这会产生性能成本。 实际上，向后扫描对工作表计划节点来说永远不是有用的，因为它不能在可滚动游标的计划树中出现得足够高，以暴露向后扫描的要求。 所以支持它并不值得付出努力。
	 *
	 * 注意：我们还假设该节点是工作表的唯一读取者。 因此，我们不需要为元组存储分配私有读取指针，也不需要告诉 tuplestore_gettupleslot 进行复制。
	 */
	Assert(ScanDirectionIsForward(fc_node->ss.ps.state->es_direction));

	fc_tuplestorestate = fc_node->rustate->working_table;

	/*
	 * 从 tuplestore 获取下一个元组。如果没有更多元组，则返回 NULL。
	 */
	fc_slot = fc_node->ss.ss_ScanTupleSlot;
	(void) tuplestore_gettupleslot(fc_tuplestorestate, true, false, fc_slot);
	return fc_slot;
}

/*
 * WorkTableScanRecheck -- 访问方法例程以在 EvalPlanQual 中重新检查元组
 */
static bool fc_WorkTableScanRecheck(WorkTableScanState *fc_node, TupleTableSlot *fc_slot)
{
	
	return true;
}


/***************************************************************
 *		ExecWorkTableScan(node)
 *
 *		顺序扫描工作表并返回下一个符合条件的元组。
 *		我们调用 ExecScan() 例程并传递适当的
 *		访问方法函数。
 ***************************************************************
 */
static TupleTableSlot * fc_ExecWorkTableScan(PlanState *fc_pstate)
{
	WorkTableScanState *fc_node = castNode(WorkTableScanState, fc_pstate);

	/*
	 * 在第一次调用时，通过为其保留的 Param 插槽找到
	 * 祖先 RecursiveUnion 的状态。 （我们无法在节点初始化期间执行此操作，因为
	 * 有一些特殊情况会导致我们在 RecursiveUnion 之前进行初始化调用。）
	 */
	if (fc_node->rustate == NULL)
	{
		WorkTableScan *fc_plan = (WorkTableScan *) fc_node->ss.ps.plan;
		EState	   *fc_estate = fc_node->ss.ps.state;
		ParamExecData *fc_param;

		fc_param = &(fc_estate->es_param_exec_vals[fc_plan->wtParam]);
		Assert(fc_param->execPlan == NULL);
		Assert(!fc_param->isnull);
		fc_node->rustate = castNode(RecursiveUnionState, DatumGetPointer(fc_param->value));
		Assert(fc_node->rustate);

		/*
		 * 扫描元组类型（即，我们期望在工作
		 * 表中找到的行类型）与祖先
		 * RecursiveUnion 节点的结果行类型相同。 请注意，这依赖于
		 * RecursiveUnion 不允许投影的假设。
		 */
		ExecAssignScanType(&fc_node->ss,
						   ExecGetResultType(&fc_node->rustate->ps));

		/*
		 * 现在我们可以初始化投影信息。 这必须在
		 * 我们可以调用 ExecScan() 之前完成。
		 */
		ExecAssignScanProjectionInfo(&fc_node->ss);
	}

	return ExecScan(&fc_node->ss,
					(ExecScanAccessMtd) fc_WorkTableScanNext,
					(ExecScanRecheckMtd) fc_WorkTableScanRecheck);
}


/* ----------------------------------------------------------------
 *		ExecInitWorkTableScan
 * ----------------------------------------------------------------
 */
WorkTableScanState *
ExecInitWorkTableScan(WorkTableScan *fc_node, EState *fc_estate, int fc_eflags)
{
	WorkTableScanState *fc_scanstate;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

	/*
	 * WorkTableScan不应该有任何子级。
	 */
	Assert(outerPlan(fc_node) == NULL);
	Assert(innerPlan(fc_node) == NULL);

	/*
	 * 为节点创建新的WorkTableScanState
	 */
	fc_scanstate = makeNode(WorkTableScanState);
	fc_scanstate->ss.ps.plan = (Plan *) fc_node;
	fc_scanstate->ss.ps.state = fc_estate;
	fc_scanstate->ss.ps.ExecProcNode = fc_ExecWorkTableScan;
	fc_scanstate->rustate = NULL;	/* 我们稍后会设置这个 */

	/*
	 * 杂项初始化
	 *
	 * 为节点创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_scanstate->ss.ps);

	/*
	 * 元组表初始化
	 */
	ExecInitResultTypeTL(&fc_scanstate->ss.ps);

	/* 信号表示返回类型尚不明确 */
	fc_scanstate->ss.ps.resultopsset = true;
	fc_scanstate->ss.ps.resultopsfixed = false;

	ExecInitScanTupleSlot(fc_estate, &fc_scanstate->ss, NULL, &TTSOpsMinimalTuple);

	/*
	 * 初始化子表达式
	 */
	fc_scanstate->ss.ps.qual =
		ExecInitQual(fc_node->scan.plan.qual, (PlanState *) fc_scanstate);

	/*
	 * 不要初始化投影信息，详见ExecWorkTableScan()。
	 */

	return fc_scanstate;
}

/* ----------------------------------------------------------------
 *		ExecEndWorkTableScan
 *
 *		释放通过C例程分配的任何存储。
 * ----------------------------------------------------------------
 */
void ExecEndWorkTableScan(WorkTableScanState *fc_node)
{
	/*
	 * 释放exprcontext
	 */
	ExecFreeExprContext(&fc_node->ss.ps);

	/*
	 * 清空元组表
	 */
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);
}

/* ----------------------------------------------------------------
 *		ExecReScanWorkTableScan
 *
 *		重新扫描关系。
 * ----------------------------------------------------------------
 */
void ExecReScanWorkTableScan(WorkTableScanState *fc_node)
{
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);

	ExecScanReScan(&fc_node->ss);

	/* 如果尚未调用ExecWorkTableScan，则无需（或无法）重新扫描 */
	if (fc_node->rustate)
		tuplestore_rescan(fc_node->rustate->working_table);
}
